#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "lobi/bmesh.h"


/* static function prototypes */

static struct bmesh_t* alloc_struct(void);
static const char* skip_after_next_white(const char* s);


/* public data */
int bmesh_errno = BMESH_E_NOERROR;


/*
 * public functions
 */


struct bmesh_t* bmesh_sread(const char* data)
{
	struct bmesh_t* mesh = NULL;
	const char* s;
	
	if (!data) goto die_invalid_param;
	
	s = data;
	mesh = alloc_struct();

	/* read header */
	{
		char cd[32]; /* content descriptor */
		char hd[32]; /* header descriptor */

		
		
		if (sscanf(s,"%s",&cd) != 1) goto die_magic;
		if (strncmp(cd,"BMESH:",6) != 0) goto die_magic;
		/* we want ascii */
		if (cd[6] == BMESH_DS_ASCII) {
			mesh->data_storage_type = BMESH_DS_ASCII;
		}
		else if(cd[6] == BMESH_DS_BINARY) {
			mesh->data_storage_type = BMESH_DS_BINARY;
		}
		else
			goto die_not_implemented;
		
		/* hd+8 contains descriptor, we only recognize VNT at the
		 * moment:P */
		if (strcmp(cd+8,"VNT") != 0) goto die_not_implemented;
		mesh->content_descriptor = BMESH_CD_VERTEX_DATA |
								   BMESH_CD_NORMAL_DATA |
								   BMESH_CD_TRIANGLE_DATA;

		/* get header descriptor */
		s = skip_after_next_white(s);
		if (sscanf(s,"%s",&hd) != 1) goto die_header_corrupt;
		if (strcmp(hd,"PT") != 0) goto die_not_implemented;
		mesh->header_descriptor = BMESH_HD_POINTS | BMESH_HD_TRIANGLES;

		/* get header data */
		if (mesh->header_descriptor & BMESH_HD_POINTS) {
			s = skip_after_next_white(s);
			if (sscanf(s,"%d",&mesh->num_points) != 1) goto die_header_corrupt;
		}
		if (mesh->header_descriptor & BMESH_HD_TRIANGLES) {
			s = skip_after_next_white(s);
			if (sscanf(s,"%d",&mesh->num_triangles) != 1) goto die_header_corrupt;
		}

		while (*s !='|') s++;
		// skip the | itself
		s++;
	}

	/*
	 * header sanity check
	 */

	if (mesh->content_descriptor & BMESH_CD_VERTEX_DATA && !(mesh->header_descriptor & BMESH_HD_POINTS))
		goto die_header_corrupt;
	if (mesh->content_descriptor & BMESH_CD_NORMAL_DATA && !(mesh->header_descriptor & BMESH_HD_POINTS))
		goto die_header_corrupt;
	if (mesh->content_descriptor & BMESH_CD_TRIANGLE_DATA && !(mesh->header_descriptor & BMESH_HD_TRIANGLES))
		goto die_header_corrupt;
	

	/* read data */
	switch (mesh->data_storage_type)
	{
		case BMESH_DS_ASCII:
		{
			int i, j;

			/* read vertex data */
			if (mesh->num_points && mesh->content_descriptor & BMESH_CD_VERTEX_DATA)
			{
				mesh->vertex_data = malloc(sizeof(float)*mesh->num_points*3);

				for (i = 0; i < mesh->num_points; i++)
				{
					for (j = 0; j < 3; j++)
					{
						float f;
						char* endptr;
						
						f = (float)strtod(s,&endptr);
						if (f == 0 && endptr == s) goto die_data_corrupt;
						
						mesh->vertex_data[i*3+j] = f;
						s = skip_after_next_white(s);
					}
				}
			}

			/* read normal data */
			if (mesh->num_points && mesh->content_descriptor & BMESH_CD_NORMAL_DATA)
			{
				mesh->normal_data = malloc(sizeof(float)*mesh->num_points*3);

				for (i = 0; i < mesh->num_points; i++)
				{
					for (j = 0; j < 3; j++)
					{
						float f;
						char* endptr;
						
						f = (float)strtod(s,&endptr);
						if (f == 0 && endptr == s) goto die_data_corrupt;

						mesh->normal_data[i*3+j] = f;
						s = skip_after_next_white(s);
					}
				}
			}

			/* read triangle data */
			if (mesh->num_triangles && mesh->content_descriptor & BMESH_CD_TRIANGLE_DATA)
			{
				mesh->triangle_data = malloc(sizeof(int)*mesh->num_triangles*3);

				for (i = 0; i < mesh->num_triangles; i++)
				{
					for (j = 0; j < 3; j++)
					{
						int k;
						char* endptr;

						k = (int)strtol(s,&endptr,10);
						if (k == 0 && endptr == s) goto die_data_corrupt;
						mesh->triangle_data[i*3+j] = k;
						s = skip_after_next_white(s);
					}
				}
			}
		}
		break;
			
		case BMESH_DS_BINARY:
		{
			size_t len;
			
			/* read vertex data */
			if (mesh->num_points && mesh->content_descriptor & BMESH_CD_VERTEX_DATA)
			{
				len = sizeof(float)*mesh->num_points*3;
				mesh->vertex_data = malloc(len);
				memmove(mesh->vertex_data,s,len);
				s += len;
			}

			/* read normal data */
			if (mesh->num_points && mesh->content_descriptor & BMESH_CD_NORMAL_DATA)
			{
				len = sizeof(float)*mesh->num_points*3;
				mesh->normal_data = malloc(len);
				memmove(mesh->normal_data,s,len);
				s += len;
			}

			/* read triangle data */
			if (mesh->num_triangles && mesh->content_descriptor & BMESH_CD_TRIANGLE_DATA)
			{
				len = sizeof(uint32_t)*mesh->num_triangles*3;
				mesh->triangle_data = malloc(len);
				memmove(mesh->triangle_data,s,len);
				s += len;
			}
		}
		break;
	}
	
	return mesh;

die_invalid_param:
	bmesh_errno = BMESH_E_INVALID_PARAM;
	goto die;
die_magic:
	bmesh_errno = BMESH_E_INVALID_MAGIC;
	goto die;
die_not_implemented:
	bmesh_errno = BMESH_E_NOT_IMPLEMENTED;
	goto die;
die_header_corrupt:
	bmesh_errno = BMESH_E_HEADER_CORRUPT;
	goto die;
die_data_corrupt:
	bmesh_errno = BMESH_E_DATA_CORRUPT;
	goto die;
	
die:
	if (mesh) bmesh_free(mesh);
	return NULL;
}

int bmesh_free(struct bmesh_t* mesh)
{
	if (!mesh) { bmesh_errno = BMESH_E_INVALID_PARAM; return 1; }

	
	if (mesh->vertex_data) free(mesh->vertex_data);
	if (mesh->normal_data) free(mesh->normal_data);
	if (mesh->triangle_data) free(mesh->triangle_data);
	free(mesh);

	
	bmesh_errno = BMESH_E_NOERROR; return 0;
}

/*
 * static functions
 */


static struct bmesh_t* alloc_struct(void)
{
	struct bmesh_t* mesh = malloc(sizeof(struct bmesh_t));
	
	mesh->content_descriptor = 0;
	mesh->header_descriptor = 0;
	
	mesh->vertex_data = NULL;
	mesh->normal_data = NULL;
	mesh->triangle_data = NULL;

	mesh->num_points = 0;
	mesh->num_triangles = 0;

	return mesh;
}

static const char* skip_after_next_white(const char* s)
{
	if (!s) return NULL;

	while (*s && !isspace(*s)) s++;
	while (*s && isspace(*s)) s++;

	return s;
}



